package com.seed.codegenerator.manager.impl;

import com.seed.codegenerator.generator.config.GenConfig;
import com.seed.codegenerator.generator.entity.FtlList;
import com.seed.codegenerator.generator.entity.PageData;
import com.seed.codegenerator.generator.entity.TemplateConfig;
import com.seed.codegenerator.generator.service.controller.ControllerGeneratorI;
import com.seed.codegenerator.generator.service.manager.ManagerGeneratorI;
import com.seed.codegenerator.generator.service.model.ModelGeneratorI;
import com.seed.codegenerator.generator.service.service.ServiceGeneratorI;
import com.seed.codegenerator.generator.type.CodeTypeEnum;
import com.seed.codegenerator.generator.type.GenTypeEnum;
import com.seed.codegenerator.manager.CodeGeneratorI;
import com.seed.codegenerator.manager.dto.GenerateDTO;
import com.seed.core.table.DataBaseTableServiceI;
import com.seed.core.table.entity.DataBaseTable;
import com.seed.core.table.entity.DataBaseTableDescribe;
import freemarker.template.Configuration;
import freemarker.template.TemplateExceptionHandler;
import lombok.extern.slf4j.Slf4j;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Service;
import org.springframework.util.StringUtils;
import org.yaml.snakeyaml.Yaml;
import org.yaml.snakeyaml.constructor.Constructor;

import java.io.File;
import java.io.IOException;
import java.io.InputStream;
import java.net.URL;
import java.util.List;


/**
 * @author gchiaway
 * 日期: 2020-02-12
 * 时间: 21:36
 */
@Slf4j
@Service("codeGeneratorService")
public class CodeGeneratorImpl implements CodeGeneratorI {

    /**
     * 控制层生成类
     */
    private final ControllerGeneratorI controllerGenerator;
    /**
     * 业务层生成类
     */
    private final ServiceGeneratorI serviceGenerator;
    /**
     * 通用业务层生成类
     */
    private final ManagerGeneratorI managerGenerator;
    /**
     * 实体生成类
     */
    private final ModelGeneratorI modelGenerator;
    /**
     * 表辅助类
     */
    private final DataBaseTableServiceI dataBaseTableService;

    /**
     * 生成配置文件
     */
    private final GenConfig genConfig;

    @Autowired
    public CodeGeneratorImpl(ControllerGeneratorI controllerGenerator,
                             ModelGeneratorI modelGenerator,
                             ServiceGeneratorI serviceGenerator,
                             ManagerGeneratorI managerGenerator,
                             DataBaseTableServiceI dataBaseTableService,
                             GenConfig genConfig) {
        this.controllerGenerator = controllerGenerator;
        this.modelGenerator = modelGenerator;
        this.serviceGenerator = serviceGenerator;
        this.managerGenerator = managerGenerator;
        this.dataBaseTableService = dataBaseTableService;
        this.genConfig = genConfig;
    }


    /**
     * 获取freemarker模板配置文件
     *
     * @param genTypeEnum 生成策略
     * @return freemarker 模板配置文件
     * @throws IOException 可能会因为找不到文件而抛出文件错误
     */
    private Configuration getConfiguration(GenTypeEnum genTypeEnum) throws IOException {
        URL resource = this.getClass().getClassLoader()
                .getResource(genConfig.getUnderResourcesTemplateFileDirectory() + genTypeEnum.getPath());
        if (StringUtils.isEmpty(resource)) {
            return null;
        }
        Configuration cfg = new Configuration();
        cfg.setDirectoryForTemplateLoading(new File(resource.getFile()));
        cfg.setDefaultEncoding("UTF-8");
        cfg.setTemplateExceptionHandler(TemplateExceptionHandler.IGNORE_HANDLER);
        return cfg;
    }

    /**
     * 根据生成策略获取模板配置
     *
     * @param genTypeEnum 生成策略
     * @return 模板配置
     */
    private TemplateConfig getTemplateConfig(GenTypeEnum genTypeEnum) throws IOException {
        Yaml yaml = new Yaml(new Constructor(TemplateConfig.class));
        InputStream resourceAsStream = this.getClass().getClassLoader().getResourceAsStream(genConfig.getUnderResourcesTemplateFileDirectory() + genTypeEnum.getPath() + "/template.yml");
        if (StringUtils.isEmpty(resourceAsStream) || resourceAsStream.available() <= 0) {
            return null;
        }
        return yaml.load(resourceAsStream);
    }

    /**
     * 根据类型生成代码
     *
     * @param outFilePath           输出目录
     * @param genTypeEnum           代码生成策略
     * @param codeTypeEnum          代码生成类型
     * @param dataBaseTableDescribe 表前缀
     * @param tableNames            表名
     * @return 生成结果
     */
    @Override
    public GenerateDTO generateCode(String outFilePath, GenTypeEnum genTypeEnum, CodeTypeEnum codeTypeEnum, DataBaseTableDescribe dataBaseTableDescribe, String... tableNames) {
        GenerateDTO result = new GenerateDTO();
        Configuration cfg;
        try {
            cfg = this.getConfiguration(genTypeEnum);
        } catch (IOException e) {
            log.error("获取Configuration出错");
            result.setSuccess(false);
            result.setMsg("获取Configuration出错");
            return result;
        }
        if (StringUtils.isEmpty(cfg)) {
            log.error("获取Configuration出错,Configuration为空");
            result.setSuccess(false);
            result.setMsg("获取Configuration出错,Configuration为空");
            return result;
        }
        TemplateConfig templateConfig;
        try {
            templateConfig = this.getTemplateConfig(genTypeEnum);
        } catch (IOException e) {
            log.error("获取template信息出错");
            result.setSuccess(false);
            result.setMsg("获取template信息出错");
            return result;
        }
        if (StringUtils.isEmpty(templateConfig)) {
            log.error("获取template信息出错,template为空");
            result.setSuccess(false);
            result.setMsg("获取template信息出错,template为空");
            return result;
        }
        FtlList template;
        switch (codeTypeEnum) {
            case SINGLE: {
                template = templateConfig.getSingle();
                break;
            }
            case ONE_TO_MANY: {
                template = templateConfig.getOneToMany();
                break;
            }
            default: {
                log.error("codeTypeEnum出错");
                result.setSuccess(false);
                result.setMsg("codeTypeEnum出错");
                return result;
            }
        }
        List<DataBaseTable> dataBaseTableList = dataBaseTableService.listDataBaseTablesByTableNamesPrefix(dataBaseTableDescribe, tableNames);
        for (DataBaseTable dataBaseTable : dataBaseTableList) {
            PageData pageData = new PageData(dataBaseTable, genConfig, genTypeEnum);
            if (!modelGenerator.genModels(outFilePath, cfg, pageData, template.getModelFtlList())) {
                result.setSuccess(false);
                result.setMsg("生成model出错");
                return result;
            }
            if (!serviceGenerator.genService(outFilePath, cfg, pageData, template.getServiceFtlList())) {
                result.setSuccess(false);
                result.setMsg("生成service出错");
                return result;
            }
            if (!managerGenerator.genManager(outFilePath, cfg, pageData, template.getManagerFtlList())) {
                result.setSuccess(false);
                result.setMsg("生成manager出错");
                return result;
            }
            if (!controllerGenerator.genController(outFilePath, cfg, pageData, template.getControllerFtlList())) {
                result.setSuccess(false);
                result.setMsg("生成controller出错");
                return result;
            }
        }
        return result;
    }


    /**
     * 生成通用代码
     *
     * @param outFilePath           输出目录
     * @param codeTypeEnum          代码生成类型
     * @param dataBaseTableDescribe 表前缀
     * @param tableNames            表名
     * @return 生成结果
     */
    @Override
    public GenerateDTO generateUniversalCode(String outFilePath, CodeTypeEnum codeTypeEnum, DataBaseTableDescribe dataBaseTableDescribe, String... tableNames) {
        return this.generateCode(outFilePath, GenTypeEnum.UNIVERSAL, codeTypeEnum, dataBaseTableDescribe, tableNames);
    }
}
